package com.xhj.mybatis.reflection.invoker;

import com.xhj.mybatis.reflection.Reflector;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * MethodInvoker
 *
 * @author XJks
 * @description 方法调用器。TODO:调用器：把“调用”抽象成统一接口。用反射调用 方法（典型用于 getter/setter）。
 */
public class MethodInvoker implements Invoker {
    /**
     * 保存方法参数类型（如果是 setter），或返回类型（如果是 getter）
     */
    private final Class<?> type;

    /**
     * 需要被调用的 Method 对象
     */
    private final Method method;

    /**
     * 构造方法，接收一个 Method 对象
     *
     * @param method 要被调用的方法
     */
    public MethodInvoker(Method method) {
        // 保存传入的 Method 对象
        this.method = method;
        // 如果方法只有一个参数（通常是 setter 方法）
        if (method.getParameterTypes().length == 1) {
            // type 设为参数类型
            type = method.getParameterTypes()[0];
        } else {
            // 否则（通常是 getter 方法），type 设为返回类型
            type = method.getReturnType();
        }
    }

    /**
     * 实现 Invoker 接口的 invoke 方法，用于调用 method 指定的方法
     *
     * @param target 表示要调用方法的对象
     * @param args   参数 args 表示调用方法时传递的参数数组
     * @return 方法调用的结果
     * @throws IllegalAccessException    如果方法调用过程中发生异常
     * @throws InvocationTargetException 如果方法调用过程中发生异常
     */
    @Override
    public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
        try {
            // 尝试直接调用方法
            return method.invoke(target, args);
        } catch (IllegalAccessException e) {
            // 如果因为权限问题调用失败
            if (Reflector.canControlMemberAccessible()) {
                // 如果可以修改方法的可访问性
                // 设置方法为可访问
                method.setAccessible(true);
                // 再次尝试调用
                return method.invoke(target, args);
            } else {
                // 如果不能修改可访问性，抛出异常
                throw e;
            }
        }
    }

    /**
     * 获取方法的参数类型（setter）或返回类型（getter）
     *
     * @return 方法的参数类型或返回类型
     */
    @Override
    public Class<?> getType() {
        return type;
    }
}
